package cn.stimd.spring.validation.beanvalidation;

import cn.stimd.spring.validation.*;
import org.hibernate.validator.messageinterpolation.ParameterMessageInterpolator;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.executable.ExecutableValidator;
import javax.validation.metadata.BeanDescriptor;
import javax.validation.metadata.ConstraintDescriptor;
import java.util.Set;

/**
 * Created by Stimd
 * 项目地址：https://gitee.com/stimd/spring-wheel
 * 公众号：Java编程探微
 */
public class SpringValidatorAdapter implements SmartValidator, Validator {
    private Validator targetValidator;

    public SpringValidatorAdapter() {
        initValidator();
    }

    @Override
    public void validate(Object target, Errors errors) {
        Set<ConstraintViolation<Object>> violations = this.targetValidator.validate(target);
        if(!violations.isEmpty()){
            processConstraintViolations(violations, errors);
        }
    }

    @Override
    public void validate(Object target, Errors errors, Object... validationHints) {
        //分组验证，略
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return (this.targetValidator != null);
    }


    protected void processConstraintViolations(Set<ConstraintViolation<Object>> violations, Errors errors) {
        for (ConstraintViolation<Object> violation : violations) {
            String field = violation.getPropertyPath().toString();
            FieldError fieldError = errors.getFieldError(field);

            //检查字段是否存在错误，如果有说明在数据绑定阶段已经报错了，则不再执行校验逻辑
            if (fieldError == null || !fieldError.isBindingFailure()) {
                ConstraintDescriptor<?> descriptor = violation.getConstraintDescriptor();
                String errorCode = descriptor.getAnnotation().annotationType().getName();   //校验注解的简单类名，比如NotBlank

                if (errors instanceof BindingResult) {
                    BindingResult bindingResult = (BindingResult) errors;

                    //对象错误
                    if("".equals(field)){
                        bindingResult.addError(new ObjectError(errors.getObjectName(), errorCode, violation.getMessage()));
                    }
                    //字段错误
                    else{
                        Object rejectedValue = violation.getInvalidValue();
                        bindingResult.addError(new FieldError(errors.getObjectName(), field,
                                rejectedValue, false, errorCode, violation.getMessage()));
                    }
                }
            }
        }
    }


    //初始化Validator
    private void initValidator() {
        this.targetValidator = Validation.byDefaultProvider().configure()
                .messageInterpolator(new ParameterMessageInterpolator())
                .buildValidatorFactory()
                .getValidator();
    }


    @Override
    public <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups) {
        return this.targetValidator.validate(object, groups);
    }

    @Override
    public <T> Set<ConstraintViolation<T>> validateProperty(T object, String propertyName, Class<?>... groups) {
        return this.targetValidator.validateProperty(object, propertyName, groups);
    }

    @Override
    public <T> Set<ConstraintViolation<T>> validateValue(Class<T> beanType, String propertyName, Object value, Class<?>... groups) {
        return this.targetValidator.validateValue(beanType, propertyName, value, groups);
    }

    @Override
    public BeanDescriptor getConstraintsForClass(Class<?> clazz) {
        return this.targetValidator.getConstraintsForClass(clazz);
    }

    @Override
    public <T> T unwrap(Class<T> type) {
        return (type != null ? this.targetValidator.unwrap(type) : (T) this.targetValidator);
    }

    @Override
    public ExecutableValidator forExecutables() {
        return this.targetValidator.forExecutables();
    }
}
